from flask import Blueprint, request, jsonify, current_app, Response
from . import database
from .services.downloader import download_manager, DownloadStatus, DownloadManager
import json
import time
from datetime import datetime
import re
from urllib.parse import urlparse
import os

bp = Blueprint('api', __name__, url_prefix='/api')
download_manager = DownloadManager()

class DateTimeEncoder(json.JSONEncoder):
    """自定义JSON编码器，用于处理datetime对象"""
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        return super().default(obj)

def api_response(code=200, message="success", data=None):
    """统一的API响应格式"""
    return jsonify({
        "code": code,
        "message": message,
        "data": data
    })

def is_valid_url(url: str) -> bool:
    """验证URL是否有效"""
    try:
        result = urlparse(url)
        return all([result.scheme, result.netloc])
    except:
        return False

def is_valid_filename(filename: str) -> bool:
    """验证文件名是否有效"""
    if not filename:
        return False
    # 检查文件名是否包含非法字符
    invalid_chars = r'[<>:"/\\|?*]'
    return not re.search(invalid_chars, filename)

def validate_download_params(data: dict) -> tuple:
    """验证下载参数"""
    if not data:
        return False, "缺少参数"
    
    if 'url' not in data:
        return False, "缺少URL参数"
    
    url = data['url']
    if not is_valid_url(url):
        return False, "无效的URL"
    
    filename = data.get('name')
    if filename and not is_valid_filename(filename):
        return False, "无效的文件名"
    
    save_dir = data.get('dir', 'downloads')
    if not os.path.exists(save_dir):
        try:
            os.makedirs(save_dir)
        except Exception as e:
            return False, f"创建下载目录失败: {str(e)}"
    
    return True, None

@bp.route('/task', methods=['GET'])
def get_downloads():
    """获取所有下载任务"""
    try:
        downloads = database.get_all_downloads()
        return api_response(data=downloads)
    except Exception as e:
        current_app.logger.error(f"获取下载任务列表错误: {str(e)}")
        return api_response(code=500, message=str(e))

@bp.route('/add', methods=['POST'])
def add_download():
    """添加下载任务"""
    try:
        data = request.get_json()
        
        # 验证参数
        is_valid, error_msg = validate_download_params(data)
        if not is_valid:
            return api_response(code=400, message=error_msg)
        
        # 检查磁盘空间
        if not database.check_disk_space():
            return api_response(code=400, message="磁盘空间不足")
        
        # 添加下载任务
        task_id = database.add_download(
            url=data['url'],
            filename=data.get('name', ''),
            save_dir=data.get('dir', 'downloads')
        )
        
        # 启动下载
        download_manager.start_download(
            task_id=task_id,
            url=data['url'],
            save_dir=data.get('dir', 'downloads'),
            headers=data.get('headers'),
            cookies=data.get('cookies'),
            proxy=data.get('proxy'),
            max_retries=int(database.get_setting('retry_count') or 3),
            timeout=int(database.get_setting('timeout') or 30)
        )
        
        return api_response(message="添加成功", data={'id': task_id})
    except Exception as e:
        current_app.logger.error(f"添加下载任务错误: {str(e)}")
        return api_response(code=500, message=str(e))

@bp.route('/del', methods=['POST'])
def delete_download():
    """删除下载任务"""
    try:
        data = request.get_json()
        if not data or 'id' not in data:
            return api_response(code=400, message="缺少必要参数")
        
        task_id = data['id']
        
        # 停止下载
        download_manager.stop_download(task_id)
        
        # 删除任务
        if database.delete_download(task_id):
            return api_response(message="删除成功")
        return api_response(code=404, message="任务不存在")
    except Exception as e:
        current_app.logger.error(f"删除下载任务错误: {str(e)}")
        return api_response(code=500, message=str(e))

@bp.route('/stop', methods=['POST'])
def stop_download():
    """停止下载任务"""
    try:
        data = request.get_json()
        if not data or 'id' not in data:
            return api_response(code=400, message="缺少必要参数")
        
        task_id = data['id']
        download = database.get_download(task_id)
        if not download:
            return api_response(code=404, message="任务不存在")
        
        # 停止下载
        download_manager.stop_download(task_id)
        
        # 更新状态
        database.update_download(task_id, status=DownloadStatus.STOPPED)
        
        return api_response(message="停止成功")
    except Exception as e:
        current_app.logger.error(f"停止下载任务错误: {str(e)}")
        return api_response(code=500, message=str(e))

@bp.route('/resume', methods=['POST'])
def resume_download():
    """恢复下载任务"""
    try:
        data = request.get_json()
        if not data or 'id' not in data:
            return api_response(code=400, message="缺少必要参数")
        
        task_id = data['id']
        download = database.get_download(task_id)
        if not download:
            return api_response(code=404, message="任务不存在")
        
        # 检查磁盘空间
        if not database.check_disk_space():
            return api_response(code=400, message="磁盘空间不足")
        
        # 启动下载
        download_manager.start_download(
            task_id=task_id,
            url=download['url'],
            save_dir=download['save_dir'],
            headers=download.get('headers'),
            cookies=download.get('cookies'),
            proxy=download.get('proxy'),
            max_retries=int(database.get_setting('retry_count') or 3),
            timeout=int(database.get_setting('timeout') or 30)
        )
        
        return api_response(message="恢复成功")
    except Exception as e:
        current_app.logger.error(f"恢复下载任务错误: {str(e)}")
        return api_response(code=500, message=str(e))

@bp.route('/events')
def events():
    """SSE事件流"""
    app = get_app()
    
    def generate():
        retry_count = 0
        max_retries = 3
        last_heartbeat = time.time()
        
        while True:
            try:
                current_time = time.time()
                
                # 发送心跳
                if current_time - last_heartbeat >= 30:  # 每30秒发送一次心跳
                    yield f"event: heartbeat\ndata: {json.dumps({'timestamp': datetime.now().isoformat()})}\n\n"
                    last_heartbeat = current_time
                
                with app.app_context():
                    downloads = database.get_all_downloads()
                    data = {
                        'downloads': downloads,
                        'timestamp': datetime.now().isoformat()
                    }
                    yield f"event: update\ndata: {json.dumps(data, cls=DateTimeEncoder)}\n\n"
                    retry_count = 0
            except Exception as e:
                app.logger.error(f"SSE错误: {str(e)}")
                retry_count += 1
                if retry_count >= max_retries:
                    yield f"event: error\ndata: {json.dumps({'message': '达到最大重试次数，连接关闭'})}\n\n"
                    break
                yield f"event: error\ndata: {json.dumps({'message': str(e)})}\n\n"
            time.sleep(1)
    
    return Response(
        generate(),
        mimetype='text/event-stream',
        headers={
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive',
            'X-Accel-Buffering': 'no'
        }
    )

@bp.route('/settings', methods=['GET'])
def get_settings():
    """获取所有设置"""
    try:
        settings = database.get_all_settings()
        return api_response(data=settings)
    except Exception as e:
        current_app.logger.error(f"获取设置错误: {str(e)}")
        return api_response(code=500, message=str(e))

@bp.route('/settings', methods=['POST'])
def update_settings():
    """更新设置"""
    try:
        data = request.get_json()
        if not data:
            return api_response(code=400, message="缺少参数")
        
        for key, value in data.items():
            database.update_setting(key, str(value))
        
        return api_response(message="更新成功")
    except Exception as e:
        current_app.logger.error(f"更新设置错误: {str(e)}")
        return api_response(code=500, message=str(e))

# 获取全局应用对象
def get_app():
    from flask import current_app
    return current_app._get_current_object() 